/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package app.net.server;

import app.game.Entity;
import app.game.FighterEntity;
import app.game.GlobalStatics;
import app.net.Client;
import app.net.messages.InputMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 * all game logic, calculate positions etc
 *
 * @author skyr
 */
public class ServerGameLoop implements Runnable {

    private ArrayList<Client> clients;
    private ArrayList<Client> players;
    // contains input message per client id used for calculations
    private HashMap<Integer, InputMessage> inputMap;
    private boolean running = true;
    private boolean isMatch = false;
    private boolean matchStarting;
    private boolean sendReset;
    private int matchTimer;
    // used for jumping physics calculations per player
    private HashMap<Integer, Boolean> isJumping;
    private HashMap<Integer, Integer> jumpingSteps;

    public ServerGameLoop() {
        // initialize maps and lists
        inputMap = new HashMap<Integer, InputMessage>();
        clients = new ArrayList<Client>();
        players = new ArrayList<Client>();
        isJumping = new HashMap<Integer, Boolean>();
        jumpingSteps = new HashMap<Integer, Integer>();
    }

    //<editor-fold defaultstate="collapsed" desc="getters setters">
    public boolean getIsMatch() {
        return isMatch;
    }

    public boolean getMatchStarting() {
        return matchStarting;
    }

    public boolean getSendReset() {
        return sendReset;
    }

    public void setSendReset(boolean sendReset) {
        this.sendReset = sendReset;
    }

    public ArrayList<Client> getClientsList() {
        return clients;
    }

    public ArrayList<Client> getPlayers() {
        return players;
    }

    public void removeClient(Client client) {
        players.remove(client);
        clients.remove(client);
    }

    public void setInputMap(int i, InputMessage inputMessage) {
        inputMap.put(i, inputMessage);
    }
    //</editor-fold>

    @Override
    public void run() {
        while (running) {
            //TODO game rules

            manageMatch();

            /*
             * if is match get input and calculate movement else ignore
             */

            if (isMatch) {
                for (Iterator<Client> it = players.iterator(); it.hasNext();) {
                    Client player = it.next();
                    /*
                     * for each player move according to input and simulate
                     * gravity forces
                     */
                    movePlayer(player);
                    simulateGravity(player.getFighterEntity());

                }
                resolveCollisions();
            }


            //finally pause for a bit
            try {
                Thread.sleep(GlobalStatics.gameTick);
            } catch (Exception e) {
            }

        }
    }

    private void movePlayer(Client player) {
        InputMessage im = inputMap.get(player.getId());
        player.getFighterEntity().move(im.getDelta());

        boolean leftClickPressed = im.isLeftClickPressed();
        boolean rightClickPressed = im.isRightClickPressed();
        boolean leftPressed = im.isLeftPressed();
        boolean rightPressed = im.isRightPressed();
        boolean jumpPressed = im.isJumpPressed();

        player.getFighterEntity().setIsAtk(leftClickPressed);
        player.getFighterEntity().setIsDef(rightClickPressed);

        player.getFighterEntity().setHorizontalMovement(0);
        if ((leftPressed) && (!rightPressed)) {
            player.getFighterEntity().setHorizontalMovement(-GlobalStatics.moveSpeed);
        } else if ((rightPressed) && (!leftPressed)) {
            player.getFighterEntity().setHorizontalMovement(GlobalStatics.moveSpeed);
        }

        player.getFighterEntity().setVerticalMovement(0);
        if (jumpPressed && isOnFloor(player.getFighterEntity())) {
            player.getFighterEntity().setVerticalMovement(-GlobalStatics.jumpSpeed);
            isJumping.put(player.getId(), true);
        }
        if (isJumping.get(player.getId()) == null) {
            jumpingSteps.put(player.getId(), 0);
            isJumping.put(player.getId(), false);
        }
        if (isJumping.get(player.getId())) {
            player.getFighterEntity().setVerticalMovement(-GlobalStatics.jumpSpeed);
            if (jumpingSteps.get(player.getId()) < GlobalStatics.maxJumpingSteps) {
                jumpingSteps.put(player.getId(), jumpingSteps.get(player.getId()) + 1);
            } else {
                jumpingSteps.put(player.getId(), 0);
                isJumping.put(player.getId(), false);
            }
        }

    }

    private void simulateGravity(Entity player) {
        if (!isOnFloor(player)) {
            player.setVerticalMovement(player.getVerticalMovement() + GlobalStatics.gravitySpeed);
        }
    }

    private boolean isOnFloor(Entity e) {
        return (e.getY() >= (GlobalStatics.W_HEIGHT - GlobalStatics.floorHeight));
    }

    private void resolveCollisions() {
        //resolve positions
        FighterEntity leftPlayer;
        FighterEntity rightPlayer;
        if (players.get(0).isIsRight()) {
            rightPlayer = players.get(0).getFighterEntity();
            leftPlayer = players.get(1).getFighterEntity();
        } else {
            rightPlayer = players.get(1).getFighterEntity();
            leftPlayer = players.get(0).getFighterEntity();
        }
        //ifs
        if (leftPlayer.collidesWith(rightPlayer)) {
            if (leftPlayer.getIsAtk() && (rightPlayer.getIsAtk() || rightPlayer.getIsDef())) {
                leftPlayer.setHorizontalMovement(-GlobalStatics.recoilSpeed * 2);
                rightPlayer.setHorizontalMovement(GlobalStatics.recoilSpeed * 2);
            } else if (leftPlayer.getIsAtk() && !rightPlayer.getIsAtk() && !rightPlayer.getIsDef()) {
                rightPlayer.setHorizontalMovement(GlobalStatics.recoilSpeed);
                rightPlayer.removeHP(1);
            } else if (leftPlayer.getIsDef() && rightPlayer.getIsAtk()) {
                leftPlayer.setHorizontalMovement(-GlobalStatics.recoilSpeed * 2);
            } else if (!leftPlayer.getIsDef() && rightPlayer.getIsAtk()) {
                leftPlayer.setHorizontalMovement(-GlobalStatics.recoilSpeed);
                leftPlayer.removeHP(1);
            } else {
                leftPlayer.setHorizontalMovement(-GlobalStatics.recoilSpeed / 2);
                rightPlayer.setHorizontalMovement(GlobalStatics.recoilSpeed / 2);
            }
        }
    }

    private void manageMatch() {
        if (!isMatch && !matchStarting) {
            if (clients.size() > 1) {
                shiftPlayers();
                matchStarting = true;
                matchTimer = GlobalStatics.matchCountDown;
                sendReset = false;
            }
        } else if (isMatch) {
            if (players.size() > 1) {
                checkFightersLives();
            } else {
                isMatch = false;
            }
        } else if (matchStarting) {
            matchTimer--;
            if (matchTimer == 0) {
                isMatch = true;
                matchStarting = false;
            }
        }
    }

    private void checkFightersLives() {
        if (players.get(0).getFighterEntity().getHealthPoints() < 0) {
            isMatch = false;
        } else if (players.get(1).getFighterEntity().getHealthPoints() < 0) {
            isMatch = false;
        }
    }

    /**
     * change player for new match
     */
    private void shiftPlayers() {
        int i;
        try {
            i = players.get(1).getId();
        } catch (Exception e) {
            i = 0;
        }
        players.clear();
        for (int j = 0; j < 2; j++) {
            i = getNextClientID(i);
            Client c = clients.get(i - 1);
            while (!c.isInQueue()) {
                i = getNextClientID(i);
                c = clients.get(i);
            }
            if (j == 0) {
                c.setIsRight(false);
                c.setFighterEntity(new FighterEntity(null, 50, 50, false));
            } else if (j == 1) {
                c.setIsRight(true);
                c.setFighterEntity(new FighterEntity(null, GlobalStatics.W_WIDTH - 50 - 60, 50, true));
            }
            players.add(c);
            System.out.println("new player " + c.getId());
        }
    }

    private int getNextClientID(int i) {
        i++;
        if (i > clients.size()) {
            i = 1;
        }
        return i;
    }

    public void start() {
        Thread t = new Thread(this);
        t.start();
    }

    public void stop() {
        running = false;
    }
}
